#include "connections.hpp"

Connection::Connection()
{
  sock = 0;
  bytes = 0;
  memset(&server_ipv4, 0, sizeof(struct sockaddr_in));
  memset(&server_ipv6, 0, sizeof(struct sockaddr_in6));
}

Connection::Connection(string ip_addr, int port)
{
  sock = 0;
  bytes = 0;
  memset(&server_ipv4, 0, sizeof(struct sockaddr_in));
  memset(&server_ipv6, 0, sizeof(struct sockaddr_in6));
  sock = this->connect_to(ip_addr, port, IPV4);
}

int Connection::connect_to(string ip_addr, int port, int ip_version = 4)
{
  char* ip = (char*) ip_addr.c_str();
  int sock_family = AF_INET;
  int sock_type = SOCK_STREAM;
  int err = 0;
  /*
   * Check the ip_version and set sock_family accordingly. It
   * is IPV4 by default. So, just check for IPV6.
   */
  if( ip_version == IPV6 ) {
    sock_family = AF_INET6;
  }

  /* Create a socket. For now, it will always by SOCK_STREAM. */
  sock = socket(sock_family, sock_type, 0);
  /* 
   * Based on whether we are using IPv4 or IPv6, fill the corresp.
   * server address structure.
   *
   */
  if( sock_family == AF_INET) {
    server_ipv4.sin_family = AF_INET;
    server_ipv4.sin_port = htons(port);
    inet_pton(AF_INET, ip, &server_ipv4.sin_addr);
    err = connect(sock, (struct sockaddr*)&server_ipv4, sizeof(server_ipv4));
  } else {
    server_ipv6.sin6_family = AF_INET6;
    server_ipv6.sin6_port = htons(port);
    inet_pton(AF_INET, ip, &server_ipv6.sin6_addr);
    err = connect(sock, (struct sockaddr*)&server_ipv6, sizeof(server_ipv6));
  }
 
  /* Connect returns -1 on error */
  if( err == -1) {
    err = errno;
    return -1;
  }
  
  return sock;
}

int Connection::send_data(string data)
{
  char* buffer = (char*) malloc(1024);
  int bytes_transferred = 0;
  int num_bytes_left = data.length();
  int err = 0;
  while(num_bytes_left > 1024) {
    memcpy(buffer, data.c_str(), 1024);
    bytes = send(sock, buffer, 1024, 0);
    if( bytes == -1) {
      err = errno;
      return bytes_transferred;
    } else {
      bytes_transferred += bytes;
      num_bytes_left -= bytes_transferred;
    }
    data = data.substr(bytes_transferred, string::npos);
  }
  memcpy(buffer, data.c_str(), data.length());
  bytes = send(sock, buffer, data.length(), 0);
  if( bytes == -1) {
    err = errno;
    return bytes_transferred;
  } else {
    bytes_transferred += bytes;
    num_bytes_left -= bytes_transferred;
  }
  return bytes_transferred;
}

string Connection::recv_data()
{
  char* buffer = (char*) malloc(1024);
  string data;
  fd_set fd_sock;
  struct timeval t;
  int sel = 0;
  int err = 0;
  FD_ZERO(&fd_sock);
  FD_SET(sock, &fd_sock);

  /* Set the select() timeout to 5 secs.*/
  t.tv_sec = 5;
  t.tv_usec = 0;

  while(1) {
    sel = select(sock+1, &fd_sock, NULL, NULL, &t);
    if( sel == -1) {
      err = errno;
    } else if(sel && FD_ISSET(sock, &fd_sock)) {
      bytes = recv(sock, buffer, 1024, 0);
      data += buffer;
    } else {
      return data;
    }
  }
}

void Connection::disconnect()
{
}
